from django.core.context_processors import csrf
from django.contrib.auth.decorators import login_required, permission_required
from django.http import Http404
from django.shortcuts import render_to_response, get_object_or_404
from django.http import HttpResponseRedirect, HttpResponse
from django.core.urlresolvers import reverse 
from django.conf import settings
from django.db.models import Q
from django.forms.models import modelformset_factory, inlineformset_factory
from django.shortcuts import redirect
from django.template import RequestContext
from django.utils.safestring import mark_safe
from django.core import serializers
from django.core.mail import EmailMultiAlternatives
from django.db.models import Sum, Avg, Max, Min
from mysite.tj3.models import * 
from mysite.settings import * 
from datetime import datetime, date
from pytz import timezone
import time
from time import mktime
import isodate # this is to handle fullcalendar dates
from itertools import groupby
from string import capwords
from django.utils import simplejson

##################################################
# projects
##################################################
from mptt.forms import *
from utility import *
from TJreport import *
from charts import *
import os.path

def graph (request):
	params={}
	params['data']=open('/tmp/data.csv','r').read()
	return render_to_response('tj3/graph_feed.html', params)

def comment(request):
	params={}
	if request.method == 'POST':
		form = UploadFileForm(request.POST, request.FILES)
		if form.is_valid():
			# do sth about the uploaded data
			data = ''.join(request.FILES['file']).strip()
			data = re.sub(r'\r\n','\n',data)
			
			# Limit to 100 records for test!
			data = '\n'.join(data.split('\n')[:20])
			open('/tmp/data.csv','w').write(data)
			
		return render_to_response('tj3/graph.html', params)
	else:
		params['form']=form = UploadFileForm()
	
	return render_to_response('tj3/comment.html', params)

def task_datatable_json(request,proj_id,scenario_id):
	# The call to this one should via AJAX post.
	proj=Project.objects.get(id=int(proj_id))
	scenario=ProjectScenario.objects.get(id=int(scenario_id))
	tasks=Task.tree.filter(project=proj)
	data=[]
	
	wbs=BuildWBS(tasks)
	for t in tasks:
		try: # container tasks do not have periods
			tp = TaskPeriod.objects.get(task=t,scenario=scenario)			
		except: tp=None
		
		deps=TaskDependency.objects.filter(from_task=t)
		ars=AllocateResource.objects.filter(task=t)
		
		d={'id':t.id,
			'wbs':wbs[t.id],
			'name':t.name,
			'desc':t.description}
		if tp:
			if tp.start:d['start']=tp.start.strftime('%m/%d/%Y %a')
			else: d['start']=''
			if tp.end: d['end']=tp.end.strftime('%m/%d/%Y %a')
			else: d['end']=''
			d['length']=tp.length
			d['duration']=tp.duration
			d['effort']=tp.effort
			if tp.max_start: d['max_start']=tp.max_start.strftime('%m/%d/%Y %a')
			else: d['max_start']=''
			if tp.min_start: d['min_start']=tp.min_start.strftime('%m/%d/%Y %a')
			else: d['min_start']=''
			if tp.max_end: d['max_end']=tp.max_end.strftime('%m/%d/%Y %a')
			else: d['max_end']=''
			if tp.min_end: d['min_end']=tp.min_end.strftime('%m/%d/%Y %a')
			else: d['min_end']=''
			d['milestone']=tp.milestone
		else:
			d['start']=''
			d['end']=''
			d['length']=''
			d['duration']=''
			d['effort']=''
			d['max_start']=''
			d['min_start']=''
			d['max_end']=''
			d['min_end']=''
			d['milestone']=''
		d['deps']=','.join(['%s %s of %s'%(dp.type, dp.on_what,wbs[dp.to_task.id]) for dp in deps])
		d['ars']=','.join(['%s %s'% (ar.primary.first_name, ar.primary.last_name) for ar in ars])
		data.append(d)				
	return HttpResponse(simplejson.dumps({'aaData':data}), content_type="application/json", mimetype='application/json')		

def task_gantt_json(request,proj_id,scenario_id):
	# The call to this one should via AJAX post.
	proj=Project.objects.get(id=int(proj_id))
	scenario=ProjectScenario.objects.get(id=int(scenario_id))
	response=[]
	tasks=Task.tree.filter(project=proj)
	wbs=BuildWBS(tasks)
	for t in tasks:
		ts = TaskStatus.objects.get(task=t,scenario=scenario)
		ld = ResourceLoad.objects.filter(task=t,scenario=scenario)
		
		response.append({"name":"%s%s"%(t.level*4*'&nbsp;',wbs[t.id]),
						"desc":"<div><font color=\"orange\">%s</font></div>"%t.name,
						"values":[{
						"from":"/Date(%s)/"%(ts.start.strftime('%s')+'000'),
						"to":"/Date(%s)/"%(ts.end.strftime('%s')+'000'),
						"desc":'''<table class="hor-minimalist-a">
							<tr><td>Name<td>%s</tr>
							<tr><td>Description<td>%s</tr>
							<tr><td>Completion<td>%s</tr>
							<tr><td>Start<td>%s</tr>
							<tr><td>End<td>%s</tr>
							</table>'''%(t.name,t.description,ts.completion,ts.start,ts.end)
						}]})
	return HttpResponse(simplejson.dumps(response), content_type="application/json", mimetype='application/json')		


def taskreport_view (request, proj_id, scenario_1, scenario_2):
	params={}
	params['project']=proj=Project.objects.get(id=int(proj_id))
	params['s1']=s1=ProjectScenario.objects.get(id=int(scenario_1))
	params['s2']=s2=ProjectScenario.objects.get(id=int(scenario_2))
	tasks = Task.tree.filter(project=proj)
	root = tasks[0]
	params['leaf_count'] = leaf_count=len(root.get_leafnodes())
	params['non_leaf_count']=len(tasks)-leaf_count	
	
	params['s1_budget']= TaskBudget.objects.get(task=tasks[0],scenario=s1)
	params['s2_budget']= TaskBudget.objects.get(task=tasks[0],scenario=s2)
	
	# Query all resources
	load_s1 = {}
	loads = ResourceLoad.objects.filter(project=proj,scenario=s1)
	for r in list(set(loads.values_list('resource', flat=True))):
		load_s1[Resource.objects.get(id=r)] = loads.filter(resource=r).aggregate(Sum('load'))['load__sum']

	load_s2 = {}
	loads = ResourceLoad.objects.filter(project=proj,scenario=s2)
	for r in list(set(loads.values_list('resource', flat=True))):
		load_s2[Resource.objects.get(id=r)] = loads.filter(resource=r).aggregate(Sum('load'))['load__sum']
	
	loads = []
	for r,load in load_s1.iteritems():
		if r in load_s2: 
			loads.append((r,load,load_s2[r]))
			del(load_s2[r]) # remove the one we have accounted for in S1
		else: loads.append((r,load,0))
	
	# if there are more resources in S2 than in S1
	for r, load in load_s2.iteritems():
		loads.append((r,0,load))
		
	params['loads']=loads
	
	# Iterate tasks to build WBS
	wbs = BuildWBS(tasks)
		
	# Iterate through tasks in tree order
	# Then iterate all scenarios
	data = []
	assignments=[]
	for t in tasks.order_by('wbs'):
		ts_1 = TaskStatus.objects.get(task=t,scenario=s1)
		ld_1 = ResourceLoad.objects.filter(task=t,scenario=s1)
		ts_2 = TaskStatus.objects.get(task=t,scenario=s2)
		ld_2 = ResourceLoad.objects.filter(task=t,scenario=s2)
		var_duration = ts_2.duration-ts_1.duration
		if ts_1.duration > 0:
			var_duration_pcnt = int(var_duration/ts_1.duration * 100)
		else: var_duration_pcnt=100
		
		var_effort = ts_2.effort - ts_1.effort
		if ts_1.effort > 0:
			var_effort_pcnt = int(var_effort/ts_1.effort*100)
		else: var_effort_pcnt=100
		
		# Add to data array
		late_start = ts_2.start - ts_1.start
		late_end = ts_2.end - ts_1.end
		data.append((wbs[t.id],t,ts_1,ld_1,ts_2,ld_2,var_duration,var_effort,var_duration_pcnt,var_effort_pcnt, late_start.days, late_end.days))
		
		# get task designs, these are raw setups that result in the schedule after TJ complilation
		# task periods
		try: # container tasks have none of these
			tp = TaskPeriod.objects.get(task=t)
			deps=TaskDependency.objects.filter(from_task=t)
			ars=AllocateResource.objects.filter(task=t)
			assignments.append((wbs[t.id],t,tp.start,tp.end,tp.length,tp.duration,tp.effort,tp.max_start,tp.min_start,tp.max_end,tp.min_end,tp.milestone,deps,ars))
		except: pass
			
	data.sort()
	assignments.sort()
	params['data']=data	
	params['assignments']=assignments
	return render_to_response('tj3/task_status_report.html', params)
	
#@login_required
def home(request):
	#if not request.user.is_authenticated():
	#	return HttpResponse('You need to login')
	
	params={}
	
	# Collect project stats, this will give user an overview of
	# what this project is about.
	projects=[]
	resources=AllocateResource.objects.all()

	for p in Project.objects.all().order_by('name'):
		# all applicable scenarios
		scenarios=p.scenario.get_descendants(include_self=False)
		
		# how many tasks?
		task_count = Task.objects.filter(project=p).count()
		
		# how many resources involved?
		tasks=Task.objects.filter(project=p)
		task_ids=[t.id for t in tasks]
		people=[]
		for r in resources:
			for t in r.task.all():
				if t.id in task_ids:
					people.append(r)
					break	
					
		projects.append((p,scenarios,task_count,people))
	
	params['projects']=projects
	return render_to_response('tj3/home.html', params)

def task_status_compare (request, proj_id, s1, s2, root=0, level=1, type='duration'):
	params={'level':level, 'type':type}
	params['project']=proj=Project.objects.get(id=int(proj_id))

	# get all scenarios selected by user, should be 2 for side-by-side comparison
	params['s1']=s1=ProjectScenario.objects.get(id=int(s1))
	params['s2']=s2=ProjectScenario.objects.get(id=int(s2))
			

	# get all tasks, here we can apply a rollup-to level
	# so 0: roll up to root, 1: roll up to level 1, and so forth..
	root=int(root)
	level=int(level)
	if not root: # root = 0
		params['tasks']=tasks=Task.tree.filter(project=proj, level__lte=1)
		params['root']=0 # Must set to 0!
	else:
		params['me']=root=Task.tree.get(id=root)
		params['root']=root.id
		params['my_parent']=root.parent
		# Advanced intersection!
		# Level is relative to the "root". So we intersect between all children tasks and tasks that fit the level requirement!
		params['tasks']=tasks=list(set(root.get_descendants(include_self=False)).intersection(Task.tree.filter(project=proj,level__lte=(root.level+level))))
		if not len(tasks): params['tasks']=tasks=[root]
		
	# build side-by-side comparison using TaskStatus data
	vals=[(TaskStatus.objects.filter(task=t,scenario=s1)[0],
			TaskStatus.objects.filter(task=t,scenario=s2)[0]) for t in tasks]
		
	# data table headers
	cols=[
		('is container','boolean','Is Container'),
		('task id','number','Task ID'),
		('task name','string','Name'),
		('scenario 1','number',s1.name),
		('scenario 2','number',s2.name),
		
		# variance, b-a
		('var','number','Variance (%s-%s)' %(s2.name, s1.name)),
		('var pcnt','number','Variance (%)'),
		]
		
	data = []
	for a,b in vals:
		if type == 'duration': # comparing duration
			aa=a.duration
			bb=b.duration
		elif type == 'effort': # comparing duration
			aa=a.effort
			bb=b.effort
		elif type == 'effortdone': # comparing duration
			aa=a.effortdone
			bb=b.effortdone
		elif type == 'effortleft': # comparing duration
			aa=a.effortleft
			bb=b.effortleft
		elif type == 'complete': # comparing duration
			# Watch! completion is an Integer field, you have to use "float" for calculations!
			aa=float(a.completion)
			bb=float(b.completion)
			
		# In some data, "aa" can be 0, making variance % a division by 0 error!
		try:
			data.append((not(a.task.is_leaf_node()),a.task.id,a.task.name,aa,bb,bb-aa,(bb-aa)/aa*100))
		except:
			data.append((not(a.task.is_leaf_node()),a.task.id,a.task.name,aa,bb,bb-aa,0))
	
	# table data
	ignore, params['json_table'] = DrawTable(cols, data, 'task_id')
	
	# line chart data
	chart_data = [tuple(a[2:-1]) for a in data]
	ignore, params['json_chart'] = DrawTable(cols[2:-1], chart_data,'task_id')
	return render_to_response('tj3/task_status_compare.html', params)


def budget_compare (request, proj_id, s1, s2, root, level=1, eac_type=1):
	# EAC type: 1- adjust by CV, 2- adjust by CPI	
	params={'level':level, 'type':eac_type}
	params['project']=proj=Project.objects.get(id=int(proj_id))

	# get all scenarios selected by user, should be 2 for side-by-side comparison
	params['s1']=s1=ProjectScenario.objects.get(id=int(s1))
	params['s2']=s2=ProjectScenario.objects.get(id=int(s2))
			
	# get all tasks, here we can apply a rollup-to level
	# so 0: roll up to root, 1: roll up to level 1, and so forth..
	root=int(root)
	level=int(level)
	if not root: # root = 0
		params['tasks']=tasks=Task.tree.filter(project=proj, level__lte=level)
		params['root']=0 # Must set to 0!
	else:
		params['me']=root=Task.tree.get(id=root)
		params['root']=root.id
		params['my_parent']=root.parent
		# Advanced intersection!
		params['tasks']=tasks=list(set(root.get_descendants(include_self=False)).intersection(Task.tree.filter(project=proj,level__lte=(root.level+level))))
		if not len(tasks): params['tasks']=tasks=[root]

	# build side-by-side comparison using TaskBudget data
	vals=[(TaskBudget.objects.filter(task=t,scenario=s1)[0],
			TaskBudget.objects.filter(task=t,scenario=s2)[0],
			TaskStatus.objects.filter(task=t,scenario=s1)[0],
			TaskStatus.objects.filter(task=t,scenario=s2)[0]) for t in tasks]
	
	# table data
	# data table headers
	cols=[
		('is container','boolean','Is Container'),
		('task id','number','Task ID'),
		('task name','string','Name'),
		('s1 budget','number','BC'),
		('s2 budget','number','AC'),
		('s1 complete','number','Scheduled (%)'),
		('s2 complete','number','Performed (%)'),
		('bcws','number','PV'),
		('bcwp','number','EV'),
		('sv','number','SV'),
		('cv','number','CV'),
		('spi','number','SPI'),
		('cpi','number','CPI'),
		('bac','number','BAC'),
		('eac','number','EAC'),
		('etc','number','ETC'),
		('tcpi bac','number','TCPI by BAC'),
		('tcpi eac','number','TCPI by EAC'),
		]

	data = []
	for a,b,c,d in vals:
		bc=a.budget
		ac=b.budget
		
		pv=bc*c.completion/100
		ev=bc*d.completion/100
		sv=ev-pv
		if pv: spi=ev/pv
		else: spi=0
		cv=ev-ac
		if ac: cpi=ev/ac
		else: cpi=0
		
		# should BAC include MR or not!?
		bac=bc + a.reserve
		
		# EAC is the manager's projection of total cost of the project at completion.
		# EAC adjusted by CV, this is kind of an incremental adjustment
		if eac_type == '1': 
			eac=bac-cv
			params['eac_type']='EAC Adjusted as [BAC-CV]'
		elif eac_type == '2':
			params['eac_type']='EAC Adjusted as [BAC/CPI]'
			# EAC adjustd by CPI, this is re-trending the forecast
			if cpi: eac=bac/cpi
			else: eac=0
		
		# ETC is the estimate to complete the remaining work of the project.
		etc=eac-ac
		
		# To-complete performance index (TCPI)
		# For the TCPI based on BAC (describing the performance required to meet the original BAC budgeted total):
		if (bac-ac): tcpi_bac=(bac-ev)/(bac-ac)
		else: tcpi_bac=0
		
		# for the TCPI based on EAC (describing the performance required to meet a new, revised budget total EAC):
		if (eac-ac): tcpi_eac=(bac-ev)/(eac-ac)
		else: tcpi_eac=0
		
		data.append((
					not(a.task.is_leaf_node()),
					a.task.id,
					a.task.name,
					a.budget,
					b.budget,
					c.completion, 
					d.completion, 
					pv,ev,sv,cv,spi,cpi,bac,eac,etc,tcpi_bac,tcpi_eac,
					))
	ignore, params['json_table'] = DrawTable(cols, data, 'task_id')
	
	# line chart data
	# data table headers
	cols=[('task name','string','Name'),
					('spi','number','SPI'),
					('cpi','number','CPI'),
					('tcpi bac','number','TCPI by BAC'),
					('tcpi eac','number','TCPI by EAC'),
					]					
	data = [(a[2],a[11],a[12],a[16],a[17]) for a in data]
	ignore, params['json_chart'] = DrawTable(cols, data,'task name')
	return render_to_response('tj3/budget_compare.html', params)

def project_performance(request,proj_id,scenario_id):
	params={}
	params['project']=proj=Project.objects.get(id=int(proj_id))
	params['base']=proj.scenario
	params['actual']=ProjectScenario.objects.get(id=int(scenario_id))
	
	# Get all resources
	resources=[]
	for s in [params['base'],params['actual']]:
		resources += [rl.resource for rl in ResourceLoad.objects.filter(project=proj,scenario=s)]
	params['resources']=list(set(resources))
	
	return render_to_response('tj3/project_performance.html',params)

def project_main_actual(request, proj_id, scenario_id):
	params={}
	params['project']=proj=Project.objects.get(id=int(proj_id))
	params['scenario']=scenario=ProjectScenario.objects.get(id=int(scenario_id))
	params['comparables']=scenario.get_ancestors()
			
	status=TaskStatus.objects.filter(scenario=scenario, project=proj)
	due=[]
	for t in status.exclude(completion=100).order_by('end'):
		if not t.task.is_leaf_node(): continue
		due.append({'end':t.end,'completion':t.completion,'task':t.task, 
					'resources':list(set([r.resource for r in ResourceLoad.objects.filter(task=t.task)]))})
	params['due']=due
	
	# Get tasks that does not have status.
	params['missing']=Task.objects.filter(project=proj).count()-len(status)
	if status:
		params['last_modified']=status[0].last_modified
		if params['missing']==0: params['ready']=True

	# Get available reports
	params['reports']=TJReport.objects.filter(project=proj)
		
	return render_to_response('tj3/project_main_actual.html',params)

def period_set (request, task_id, scenario_id, type='start', complete=0):
	params={}
	params['task']=task=Task.objects.get(id=int(task_id))
	params['scenario']=scenario=ProjectScenario.objects.get(id=int(scenario_id))

	# Look up TaskPeriod
	tp, created = TaskPeriod.objects.get_or_create(task=task,scenario=scenario)
	if type=='start': # starting a task
		# If we are starting a task,
		# set the "start" to be TODAY
		tp.start = date.today()
		tp.complete=0
	elif type=='end' or complete == 100: # ending a task
		# If we are ending a task,
		# set the "end" to be TODAY,
		# and complete = 100	
		tp.end = date.today()
		tp.complete=100
		
	elif type=='update': # updating complete
		tp.complete = complete
		tp.end=None	
	# Push to DB	
	tp.save()
	params['period']=tp
	return render_to_response('tj3/period_set.html',params)	

def project_compile (request, proj_id, scenario_id):
	params={}
	params['project']=proj=Project.objects.get(id=int(proj_id))
	params['scenario']=scenario=ProjectScenario.objects.get(id=int(scenario_id))
	params['taskreports']=task_reports=TaskReport.objects.filter(project=proj)
	params['resourcereports']=resource_reports=ResourceReport.objects.filter(project=proj)
	
	tjp,task_tji,resource_tji,journal_tji,report_tji,customer_reports,stdout, stderr, error_parsed = CompileProject(proj, scenario, task_reports, resource_reports)
	params['stdout']= stdout
	params['stderr']= stderr.strip()
	params['tjp']=open(os.path.join(proj.working_path,tjp)).read()
	params['task_tji']=open(os.path.join(proj.working_path,task_tji)).read()
	params['resource_tji']=open(os.path.join(proj.working_path,resource_tji)).read()
	params['journal_tji']=open(os.path.join(proj.working_path,journal_tji)).read()
	params['report_tji']= open(os.path.join(proj.working_path,report_tji)).read()

	params['error_parsed']=error_parsed
	if error_parsed:
		temp=TJError.objects.filter(project=proj)
		params['errors']=temp.filter(type__contains='error')
		params['warnings']=temp.filter(type__contains='warning')

	if customer_reports:
		params['reports']=[TJReport.objects.get(project=proj,template=r) for id,r in customer_reports]

	return render_to_response('tj3/project_compile_debug.html',params)

def resource_load (request, proj_id, resource_id, s1,s2, type='weekly'):
	params={}
	params['project']=proj=Project.objects.get(id=int(proj_id))
	params['type']=type

	# get all scenarios selected by user, should be 2 for side-by-side comparison
	params['s1']=s1=ProjectScenario.objects.get(id=int(s1))
	params['s2']=s2=ProjectScenario.objects.get(id=int(s2))
			
	# get all project task load for this scenario
	loads={}
	if int(resource_id):
		params['resource']=resource=Resource.objects.get(id=int(resource_id))
		loads[s1]=ResourceLoad.objects.filter(project=proj, scenario=s1, resource=resource)
		loads[s2]=ResourceLoad.objects.filter(project=proj, scenario=s2, resource=resource)
	else: 
		loads[s1]=ResourceLoad.objects.filter(project=proj, scenario=s1)
		loads[s2]=ResourceLoad.objects.filter(project=proj, scenario=s2)
	
	# group loads
	data = {}
	for (s,l_list) in loads.items():
		for l in l_list:
			yr=l.when.year
			mo=l.when.month
			day=l.when.day
			
			if type=='daily':
				group=l.when
			elif type == 'weekly':
				# get week
				group=date(yr,mo,day).isocalendar()[1]
			elif type=='monthly':
				group=mo
			elif type=='quarterly':
				group=(mo-1)//3+1
				
			if group not in data: data[group]=[0,0]
			if s is s1: data[group][0] +=l.load
			elif s is s2: data[group][1] += l.load
	
	cols=[('type','string',type),('s1','number',s1.name),('s2','number',s2.name),('var','number','Variance (%s-%s)' %(s2.name,s1.name))]
	ignore, params['json_table'] = DrawTable(cols, [(a,b,c,c-b) for (a,[b,c]) in data.items()], 'type')
			
	return render_to_response('tj3/resource_load.html', params)
	
def task_hierarchy(request, proj_id):
	params={}
	params['project']=proj = Project.objects.get(id=int(proj_id))
	params['scenarios']=proj.scenario.get_descendants(include_self=False)
	params['tasks']=tasks = Task.tree.filter(project=proj).order_by('wbs','lft')
	params['form']=form = UploadFileForm()
			
	if request.method == 'POST':
		form = UploadFileForm(request.POST, request.FILES)
		if form.is_valid():
			ImportMPP(proj, request.FILES['file'])
			
			# do sth about the uploaded data
			return redirect('task_hierarchy', proj_id=proj.id)
		else:
			return redirect('home')
	else:
		params['form']=form = UploadFileForm()

		# line chart data
		cols=[('name','string','Name'),('parent','string','Parent'),('tooltip','string','ToolTip')]
		chart_data=[]
		for t in tasks:
			self_id='%d-%s' %(t.id,t.name)
			if t.parent:
				parent_id='%d-%s'%(t.parent.id,t.parent.name)
			else: parent_id=''
			chart_data.append((self_id,parent_id,''))
		ignore, params['json'] = DrawTable(cols, chart_data,'name')	
		return render_to_response('tj3/task_hierarchy.html',params,context_instance=RequestContext(request))

def task_detail(request, task_id):
	params={}
	params['task']=task=Task.objects.get(id=int(task_id))
	params['is_leaf']=is_leaf=task.is_leaf_node()
	
	if request.method=='POST':
		pass	
	else:
		params['periods']=TaskPeriod.objects.filter(task=task)
		params['we_deps']=TaskDependency.objects.filter(from_task=task)
		params['they_deps']=TaskDependency.objects.filter(to_task=task)
		
		# only leaf task should have this option
		if is_leaf:
			params['budgets']=TaskBudget.objects.filter(task=task).order_by('scenario')
								
		resources = [(r,True) for r in AllocateResource.objects.filter(task=task)]
		# Resources can be inherited. But the inherited ones should not be editable!
		for a in task.get_ancestors():
			resources += [(r,False) for r in AllocateResource.objects.filter(task=a)]
		params['resource_assigned']=resources
		
		# tags
		params['tags']=task.tags.all()	
		
		# Calcuated status
		params['status']=status=TaskStatus.objects.filter(task=task)
		if len(status):
			params['updated_on']= status[0].last_modified
		
		# Existing bookings and journals
		bookings=Booking.objects.filter(task=task).order_by('-when')
		
		# group by booking.when
		params['journals']=[]
		for key, group in groupby(bookings, lambda x: x.when):
			params['journals'].append((key,[Journal.objects.filter(booking=b) for b in group]))
	
		return render_to_response('tj3/task_detail.html',params)

def budget_create(request,task_id):
	params={}
	params['task']=task=Task.objects.get(id=int(task_id))	
	BudgetFormset=inlineformset_factory(Task, TaskBudget,extra=1,can_delete=False)
	params['budgets']=budgets=TaskBudget.objects.filter(task=task).order_by('scenario')
	params['budget_formset']=BudgetFormset()

	if request.method=='POST':
		new_budget=BudgetFormset(request.POST,instance=task)
		if request.POST['command'].lower()=='create' and  new_budget.is_valid(): 
			instance=new_budget.save(commit=False)[0]
			
			if TaskBudget.objects.filter(task=task,scenario=instance.scenario):
				params['duplicate_scenario']=instance.scenario
				return render_to_response('tj3/budget_create.html',params)
			else:	
				instance.save()
				
				# This is a BIG deal!! 
				# As long as one node in the tree is setting up a BUDGET, all nodes need to set up a budget!!!
				# The good news, is that you should need to do this only ONCE per scenario.
				# Just pick a node and create a budget, and walah, the entire tree is set up!
				#for t in Task.objects.filter(project=instance.task.project): # everybody
				#	b,created=TaskBudget.objects.get_or_create(task=t,scenario=instance.scenario)

				for t in Task.objects.filter(project=instance.task.project): # everybody
					if t.is_leaf_node():	
						# call a utility function, which walks the tree to update container budgets
						UpdateBudget(t, instance.scenario)
			
		return redirect('task_detail',task_id=task.id)
	else:
		return render_to_response('tj3/budget_create.html',params)	

def budget_edit(request,budget_id):
	params={}
	params['budgets']=budgets=TaskBudget.objects.filter(id=int(budget_id))
	params['task']=task=budgets[0].task
	params['scenario']=budgets[0].scenario
	
	BudgetFormset=modelformset_factory(TaskBudget,exclude=('task', 'scenario'),extra=0)
	
	if request.method=='POST':
		new_budget=BudgetFormset(request.POST)
		if request.POST['command'].lower()=='update' and new_budget.is_valid():
			instance = new_budget.save()
			if instance:
				# call utility function to update all budgets
				UpdateBudget(instance[0].task, instance[0].scenario)
			else: # form data was not changed!
				pass
			
		elif request.POST['command'].lower()=='delete':
			params['budgets'].delete()
		elif request.POST['command'].lower()=='cancel':
			pass
		return redirect('task_detail',task_id=task.id)
	else:
		params['budget_formset']=BudgetFormset(queryset=budgets)
		
	return render_to_response('tj3/budget_edit.html',params)

def period_create(request,task_id):
	params={}
	params['task']=task=Task.objects.get(id=int(task_id))	
	PeriodFormset=inlineformset_factory(Task, TaskPeriod,extra=1,can_delete=False)
	params['periods']=periods=TaskPeriod.objects.filter(task=task)
	params['period_formset']=PeriodFormset()

	if request.method=='POST':
		new_period=PeriodFormset(request.POST,instance=task)
		if request.POST['command'].lower()=='create' and new_period.is_valid(): 
			instance=new_period.save(commit=False)[0]

			if TaskPeriod.objects.filter(task=task,scenario=instance.scenario):
				params['duplicate_scenario']=instance.scenario
				return render_to_response('tj3/period_create.html',params)
			else:
				instance.save()
				
		return redirect('task_detail',task_id=task.id)
	else:
		return render_to_response('tj3/period_create.html',params)	

def period_edit(request,period_id, type=0):
	params={}
	params['period']=period=TaskPeriod.objects.filter(id=int(period_id)).order_by('scenario')
	params['task']=period[0].task
	params['scenario']=period[0].scenario
	
	if int(type) is 0:	
		PeriodFormset=modelformset_factory(TaskPeriod,
			exclude=('task','scenario'),extra=0)
	elif int(type) is 1:
		PeriodFormset=modelformset_factory(TaskPeriod,
			exclude=('task','scenario','max_start','min_start','max_end','min_end','complete','milestone','length','duration','effort'),extra=0)
	elif int(type) is 2:
		PeriodFormset=modelformset_factory(TaskPeriod,
			exclude=('task','scenario','max_start','min_start','max_end','min_end','complete','milestone','start','end'),extra=0)
	elif int(type) is 3:
		PeriodFormset=modelformset_factory(TaskPeriod,exclude=('task','scenario','start','end','complete','milestone','duration','length','effort'),extra=0)
	
	if request.method=='POST':
		new_period=PeriodFormset(request.POST)
		if request.POST['command'].lower()=='update' and new_period.is_valid():
			new_period.save()
		elif request.POST['command'].lower()=='delete':
			params['period'].delete()
		return redirect('task_detail',task_id=params['task'].id)
	else:
		params['period_formset']=PeriodFormset(queryset=period)
		
	return render_to_response('tj3/period_edit.html',params)

def resource_allocate_remove(request, task_id,r_id):
	a = AllocateResource.objects.get(id=int(r_id))
	a.task.remove(int(task_id))
	a.save()
	return redirect('task_detail',task_id)
	
def resource_allocate(request, task_id):
	params={}
	params['task']=task=Task.objects.get(id=int(task_id))		

	# Get allocated resources
	params['allocated']=allocated=AllocateResource.objects.filter(task=task)
	temp_list=[a.primary for a in allocated]
	for a in allocated:
		if a.alternative: temp_list.append(a.alternative)
	allocated=list(set(temp_list))
	last_names=[a.last_name for a in allocated]
	first_names=[a.first_name for a in allocated]

	# This needs to be tuned later when users are linked to a project.
	# As now, it will retrieve all resources in the DB.
	params['resources']=Resource.objects.exclude(last_name__in =last_names).exclude(first_name__in=first_names).order_by('first_name')
		
	if request.method=='POST':
		# Validation check
		# You can NOT assign the same PERSON twice!
		# Same person can have multiple instance of AllocateResource due to different worklimit speicified.
		# But for a task, a person can only be assigned ONCE!
		resources=[Resource.objects.get(id=int(a)) for a in request.POST.getlist('resource')]
		name_list=[(r.last_name,r.first_name) for r in resources]
		if len(set(name_list)) < len(resources):
			params['duplicate_resource']=True
			return render_to_response('tj3/resource_allocate.html',params)
		else:
			for r in resources:
				b,created=AllocateResource.objects.get_or_create(primary=r)
				b.task.add(task)
				b.save()
		return redirect('task_detail',task_id=task.id)
	else:
		return render_to_response('tj3/resource_allocate.html',params)
	
def report_display (request, report_id):
	params={}
	params['report']=TJReport.objects.get(id=int(report_id))
	return render_to_response('tj3/report_display.html',params)

def task_move (request):
	# The call to this one should via AJAX post.
	params={}
	if request.is_ajax():
		if request.method == 'POST':
			# json_data contains your objects
			#response = simplejson.dumps(json_data)
			json_data = simplejson.loads(request.POST.get('data',False))			
			response=json_data
			json_data=simplejson.loads(json_data)
			UpdateTaskHierarchy(json_data,None)
			return HttpResponse(response)			
	
def UpdateTaskHierarchy (id_list, root):
	# json_data will be a nested task id lists
	for i in range(len(id_list)):
		data=id_list[i]
		if isinstance(data,dict): me=int(data['root'])
		else: me = int(data)
		
		me=Task.objects.get(id=me)
		if root: me.wbs=i+1
		else: me.wbs=1
		me.save()
			
		# move node
		if root: root=Task.objects.get(id=root.id)
		me.move_to(root,'last-child')

		# recurse the hierarchy
		if isinstance(data,dict):
			UpdateTaskHierarchy(data['children'],me)

def project_main_plan(request, proj_id):
	#params={}
	#params['project']=proj=Project.objects.get(id=int(proj_id))
	#params['scenarios']=proj.scenario.get_descendants(include_self=False)
	#params['form']=form = UploadFileForm()
	
	# table data
	# data table headers
	#cols=[('task id','number','Task ID'),
	#		('task name','string','Name'),
	#	]
	
	#data=[]
	#for t in Task.tree.filter(project=proj):
	#	data.append((t.id,'&nbsp;'*t.level*4+t.name))
	
	#ignore, params['json_table'] = DrawTable(cols, data,'')
	#return render_to_response('tj3/project_main_plan.html',params)
	return redirect('task_hierarchy',proj_id)

def task_edit(request):
	params={}
	
	if request.is_ajax():
		if request.method == 'POST':
			id = request.POST.get('id',False)
			
			# Get task instance
			params['id']=id=int(id)
			task=Task.objects.filter(id=int(id))
			params['task']=serializers.serialize('json',task)
			
			scenario=task[0].project.scenario
			
			# Get task periods
			period=TaskPeriod.objects.filter(task=task,scenario=scenario)
			params['period']=serializers.serialize('json',period)

			# Deps
			pat=re.compile('(?P<lag>-?)(?P<num>\d+)(?P<unit>min|h|d|w|m|y)')
			deps={}
			for d in TaskDependency.objects.filter(from_task=task[0]):
				if d.type=='depends' and d.on_what=='onend':
					a='%dFS' % d.to_task.id
				elif d.type=='depends' and d.on_what=='onstart':
					a='%dSS' % d.to_task.id
				elif d.type=='precedes' and d.on_what=='onend':
					a='%dFF' % d.to_task.id
				elif d.type=='precedes' and d.on_what=='onstart':
					a='%dSF' % d.to_task.id
				else: a='unknown'
				
				if d.gap_length: 
					temp=pat.match(d.gap_length)
					if temp:
						if not temp.group('lag'): a+='+'
						a += temp.group('lag')+temp.group('num')+temp.group('unit')
				elif d.gap_duration:
					temp=pat.match(d.gap_duration)
					if temp:
						if not temp.group('lag'): a+= '+'
						# The letter 'e' indicates it's by calendar time, thus "duration" in TJ
						a += temp.group('lag')+temp.group('num')+'e'+temp.group('unit') 
				
				# There won't be duplicate link to a target task,
				# so it's safe to use it as dict key.
				deps[d.to_task.id]=a
			params['deps']=deps
			
			# Return the data
			return HttpResponse(simplejson.dumps(params), mimetype="application/json")

def task_split(request, task_id):
	'''
	Splitting a task takes 3 steps:
	1. Create a dummy container task, name it "dummy container (split)"
	2. Move this task to be the child of this dummy container task
	3. Create a new child task, name it "dummy child (split)"
	'''
	
	task=Task.objects.get(id=int(task_id))
	dummy_container=Task(name='Dummy Container (split)', 
						description='This is a dummy container resulted from a task split.',
						project=task.project,
						parent=task.parent)
	dummy_container.save()
	
	new_child=Task(name='New Task (split)', 
					description='This is a dummy task resulted from a task split.', 
					project=task.project,
					parent=dummy_container)
	new_child.save()
	
	task.parent=dummy_container
	task.save()

	return redirect('task_hierarchy',proj_id=task.project.id)
	

def task_update (request, clone):
	# Change task name
	task=Task.objects.get(id=int(request.POST['task_id']))
	if int(clone):
		task=Task(project=task.project, parent=task.parent)
		task.name='%s (cloned)' % request.POST['task_name']
	else:
		task.name=request.POST['task_name']
	task.description=request.POST['task_description']
		
	# Get parent
	try: 
		task.parent=Task.objects.get(id=int(request.POST['task_parent']))
	except: pass
	
	# Save the changes
	task.save()
	
	# Get task budget
	budget, created=TaskBudget.objects.get_or_create(task=task,scenario=task.project.scenario)
	
	# Get period
	time_format='%Y-%m-%d'
	period, created=TaskPeriod.objects.get_or_create(task=task,scenario=task.project.scenario)
	if request.POST['start']:
		period.start=datetime.fromtimestamp(time.mktime(time.strptime(request.POST['start'],time_format)))
	if request.POST['end']:
		period.end=datetime.fromtimestamp(time.mktime(time.strptime(request.POST['end'],time_format)))
	period.duration=request.POST['duration'].lower()
	period.length=request.POST['length'].lower()
	period.effort=request.POST['effort'].lower()
	
	if request.POST['max_start']:
		period.max_start=datetime.fromtimestamp(time.mktime(time.strptime(request.POST['max_start'],time_format)))
	else: period.max_start=None
	if request.POST['min_start']:
		period.min_start=datetime.fromtimestamp(time.mktime(time.strptime(request.POST['min_start'],time_format)))
	else: period.min_start=None
	if request.POST['max_end']:
		period.max_end=datetime.fromtimestamp(time.mktime(time.strptime(request.POST['max_end'],time_format)))
	else: period.max_end=None
	if request.POST['min_end']:
		period.min_end=datetime.fromtimestamp(time.mktime(time.strptime(request.POST['min_end'],time_format)))	
	else: period.min_end=None
	period.save()
	
	# Get depends
	pat=re.compile('(?P<to_task>\d+)(?P<type>fs|ss|ff|sf)(?P<gap>.*)')
	gap_pat=re.compile('(?P<lag>[-+]?)(?P<num>\d+)(?P<e>e?)(?P<unit>min|h|d|w|m|y)')
	# If we are receiving deps, we need first purge all existing deps for this task!
	if request.POST['deps']:
		TaskDependency.objects.filter(from_task=task).delete()
		
	# Now read the new deps and set them up
	dep_string=re.sub('\s+','',request.POST['deps']).lower() # strip all white spaces
	for d in dep_string.split(','):
		temp=pat.match(d.lower())
		if temp:
			# Create dep instance using to_task
			to_task=Task.objects.get(id=int(temp.group('to_task')))
			dep, created=TaskDependency.objects.get_or_create(from_task=task,to_task=to_task)
			
			# Create dep type and on_what
			if temp.group('type')=='fs':
				dep.type='depends'
				dep.on_what='onend'
			elif temp.group('type')=='ss':
				dep.type='depends'
				dep.on_what='onstart'
			elif temp.group('type')=='ff':
				dep.type='precedes'
				dep.on_what='onend'
			elif temp.group('type')=='sf':
				dep.type='precedes'
				dep.on_what='onstart'
				
			# Any gap?
			if temp.group('gap'):
				g=gap_pat.match(temp.group('gap'))
				if g:
					# Lag mark "+-" is not allowed by TJ, reserve them here for now.
					if g.group('e'): # for gap_duration
						dep.gap_duration= g.group('num')+g.group('unit')
					else: # for gap_length
						dep.gap_length= g.group('num')+g.group('unit')
			dep.save()	
	return redirect('task_hierarchy',proj_id=task.project.id)

def task_delete(request, task_id):
	params={}
	params['task']=task=Task.objects.get(id=int(task_id))
	
	if request.method=='POST':
		proj_id=task.project.id
		task.delete()
		return redirect('task_hierarchy',proj_id=proj_id)
	else:
		return render_to_response('tj3/task_delete.html',params)

def dep_delete(request, dep_id):
	params={}
	params['dep']=dep=TaskDependency.objects.get(id=int(dep_id))
	task=dep.from_task
	dep.delete()
	return redirect('task_detail', task_id=task.id)

def dep_edit (request, dep_id):
	params={}
	params['dep']=dep=TaskDependency.objects.get(id=int(dep_id))
	params['task']=task=dep.from_task
	params['project']=proj=task.project
	
	if request.method=='POST':
		if request.POST['command'].lower()=='delete':
			dep.delete()
		elif request.POST['command'].lower()=='update':
			dep.to_task=Task.objects.get(id=int(request.POST['to_task']))
			dep.type=request.POST.getlist('type')[0]
			dep.on_what=request.POST.getlist('on_what')[0]
			dep.gap_duration=request.POST['duration']
			dep.gap_length=request.POST['length']
			dep.save()
		return redirect('task_detail',task_id=task.id)
	else:
		# table data
		# data table headers
		cols=[('task id','number','Task ID'),
				('task name','string','Name'),
			]
		
		data=[]
		for t in Task.tree.filter(project=proj):
			data.append((t.id,'&nbsp;'*t.level*4+t.name))
		
		ignore, params['json_table'] = DrawTable(cols, data,'')
	
	return render_to_response('tj3/dep_edit.html',params)

def project_calendar(request, proj_id, task_id, resource_id):
	params={}
	params['project']=proj=Project.objects.get(id=int(proj_id))
	params['task']=task=Task.objects.get(id=int(task_id))
	params['resource']=resource=Resource.objects.get(id=int(resource_id))
	
	return render_to_response('tj3/project_calendar.html', params)
  
def project_calendar_feeder(request, proj_id):
	params={}
	params['project']=proj=Project.objects.get(id=int(proj_id))
	
	events=[]
	for t in Task.objects.filter(project=proj):
		for b in Booking.objects.filter(task=t):
			for j in Journal.objects.filter(booking=b):
				g=float(str(b.part_of_day).split(':')[0])+float(b.length[:-1])
				events.append({	'id': j.id,
								'title': '%s (%d,%d): %s. %s' % (t.name, t.id, b.resource.id, j.summary, j.description),
								'start': b.when.strftime('%Y-%m-%d') + ' '+str(b.part_of_day),
								'end': b.when.strftime('%Y-%m-%d') + ' %d:%d' % (int(g),(g-int(g))*60),
								'allDay':False,
								'editable':True,
								'url': '/tj3/journal/edit/%d/' % j.id,
								})							
	return HttpResponse(simplejson.dumps(events), mimetype="application/json")

def project_calendar_booking(request):
	params={}
	
	# TODO: We are converting everyting to US EST right now.
	# Once a user account is established, this should be a varialbe!!
	eastern = timezone('US/Eastern')
	pat=re.compile('\((?P<id>[^)]*)\)\s?:\s?(?P<content>.*)')

	if request.is_ajax():
		if request.method == 'POST':
			e = simplejson.loads(request.POST.get('data',False))
			start=end=None
			
			# Get start and end
			if e['start']:
				start = isodate.parse_datetime(e['start'])
				start= start.astimezone(eastern)
			if e['end']:
				end= isodate.parse_datetime(e['end'])
				end = end.astimezone(eastern)
				
			# Get the title which has embedded task_id and resource_id
			temp=pat.search(e['title'])
			if temp:
				aa=temp.group('id').split(',')
				if len(aa) == 2:
					task=Task.objects.get(id=int(aa[0].strip()))
					resource=Resource.objects.get(id=int(aa[1].strip()))
							
					if e.has_key('id'): # For existing booking, there is a journal id
						journal=Journal.objects.get(id=int(e['id']))
						booking=journal.booking
						booking.when=start
						
						# convert start and end to part_of_day and length
						# this is just pain.
						if start.hour <12:
							daypart=Daypart.objects.get(name__contains='morning')
						elif start.hour <15:
							daypart=Daypart.objects.get(name__contains='mid')
						else:
							daypart=Daypart.objects.get(name__contains='afternoon')
						booking.part_of_day=daypart
						if start and end: length= '%2.1fh' % ((end-start).total_seconds()/3600) 
						else: length='1h' # default to 1 hour!
						booking.length=length
						booking.save()
						
					else: # new booking
						# convert start and end to part_of_day and length
						# this is just pain.
						if start.hour <12:
							daypart=Daypart.objects.get(name__contains='morning')
						elif start.hour <18:
							daypart=Daypart.objects.get(name__contains='afternoon')
						else:
							daypart=Daypart.objects.get(name__contains='afternoon')
						booking,created = Booking.objects.get_or_create(task=task,resource=resource, when=start, part_of_day=daypart)
						
						if start and end: length= '%2.1fh' % ((end-start).total_seconds()/3600) 
						else: length='0h'
						booking.length=length						
						booking.save()
						
						journal =Journal(booking=booking)
						# Split the content by a period. First part is the summary,
						# rest of it is the descriptions.
						bb=temp.group('content').split('.')
						journal.summary=bb[0].strip()
						journal.description=' '.join(bb[1:])
						journal.save()						

					# Compose an event object and give it back to the client
					g=float(str(booking.part_of_day).split(':')[0])+float(booking.length[:-1])
					event={	'id': journal.id,
							'title': '%s (%d,%d): %s. %s' % (task.name, task.id, booking.resource.id, journal.summary, journal.description),
							'start': booking.when.strftime('%Y-%m-%d') + ' '+str(booking.part_of_day),
							'end': booking.when.strftime('%Y-%m-%d') + ' %d:%d' % (int(g),(g-int(g))*60),
							'editable':True,
							'allDay':True, # will be overidden if actual end time is set!
							'url':'/tj3/journal/edit/%d/' % journal.id,
						}
					if end: 
						event['allDay']=False									
					params['event']=event
					# Return the data
					return HttpResponse(simplejson.dumps(params), mimetype="application/json")
			
def journal_edit (request, journal_id):
	params={}
	journal=Journal.objects.filter(id=int(journal_id))
	params['booking']=booking=journal[0].booking
	params['project']=proj=journal[0].booking.task.project
	params['task']=journal[0].booking.task
	params['resource']=journal[0].booking.resource
	
	JournalFormset = modelformset_factory(Journal, exclude=('booking',),extra=0)
	if request.method=='POST':
		new_journal=JournalFormset(request.POST)
		if request.POST['command'].lower()=='update' and new_journal.is_valid():
			new_journal.save()
		elif request.POST['command'].lower()=='delete':
			return redirect('journal_delete', journal_id=journal[0].id)
		return redirect('booking_edit',booking_id=booking.id)
	else:
		params['journal_formset']=JournalFormset(queryset=journal)
	return render_to_response('tj3/journal_edit.html',params)	
	
def journal_delete(request, journal_id):
	params={}
	params['journal']=journal=Journal.objects.get(id=int(journal_id))
	booking=journal.booking
	journal.delete()
			
	return redirect('booking_edit', booking_id=booking.id)

def journal_create(request,booking_id):
	params={}
	params['booking']=booking=Booking.objects.get(id=int(booking_id))
	params['project']=booking.task.project
	params['resource']=booking.resource
	params['task']=booking.task
		
	JournalFormset=inlineformset_factory(Booking, Journal,extra=1,can_delete=False)
	if request.method=='POST':
		cmd=request.POST['command'].lower()
		new_journal=JournalFormset(request.POST,instance=booking)
		if cmd=='create' and new_journal.is_valid(): new_journal.save()	
		return redirect('booking_edit',booking_id=booking_id)
	else:
		params['journal_formset']=JournalFormset()
		return render_to_response('tj3/journal_create.html',params)	
	
def booking_edit (request, booking_id):
	params={}
	params['booking']=booking=Booking.objects.filter(id=int(booking_id))
	params['task']=Task.objects.get(id=booking[0].task.id)
	
	BookingFormset = modelformset_factory(Booking, exclude=('resource','task','overtime'), extra=0)
	if request.method=='POST':
		cmd=request.POST['command'].lower()
		new_booking=BookingFormset(request.POST)
		if cmd =='update' and new_booking.is_valid():
			new_booking.save()
		elif cmd=='delete':
			booking.delete()
		return redirect('task_detail', task_id=params['task'].id)
	else:
		params['booking']=booking[0]
		params['booking_formset']=BookingFormset(queryset=booking)
		params['journals']=[{'id':j.id, 'created':j.created, 'summary':j.summary,'description':j.description} for j in Journal.objects.filter(booking=booking)]
		
	return render_to_response('tj3/booking_edit.html',params)	

def resource_edit(request, resource_id):
	params={}
	params['resource']=resource=Resource.objects.filter(id=int(resource_id))
	ResourceFormset=modelformset_factory(Resource,extra=0)
	if request.method=='POST':
		new_resource=ResourceFormset(request.POST)
		cmd=request.POST['command'].lower()
		
		if cmd =='update' and new_resource.is_valid():
			new_resource.save()
		elif cmd =='delete':
			resource.delete()
		return redirect('resource_manage')
	else:
		params['resource_formset']=ResourceFormset(queryset=resource)
	return render_to_response('tj3/resource_edit.html',params)

def resource_create(request):
	params={}
	ResourceFormset=modelformset_factory(Resource,extra=1)
	WorklimitFormset=modelformset_factory(WorkLimit,extra=1)

	# get allocated resource
	if request.method=='POST':
		new_resource=ResourceFormset(request.POST,prefix='resource')
		new_worklimit=WorklimitFormset(request.POST,prefix='worklimit')
		
		if new_resource.is_valid():
			r=new_resource.save(commit=False)[0]
			
			# if we definie a new worklimit
			# assuming that this should be used by this resource	
			if new_worklimit.is_valid():
				w = new_worklimit.save()
				if len(w):
					r.work_limit=w[0]
		
			# save resource & resource taggs
			r.save()
			new_resource.save_m2m()
				
			# auto create an AllocateResouce
			# the whole purpose of creating a resource is to use him.
			new_alloc=AllocateResource()
			new_alloc.primary=r
			new_alloc.save()
			return redirect('resource_manage')	
		else:
			return redirect('resource_create')
	else:
		params['resource_formset']=ResourceFormset(queryset=Resource.objects.none(),prefix='resource')
		params['worklimit_formset']=WorklimitFormset(queryset=WorkLimit.objects.none(),prefix='worklimit')
		return render_to_response('tj3/resource_create.html',params)	

def resource_manage(request):
	# TODO: this function isn't going anywhere. 
	params={}
	return render_to_response('tj3/resource_manage.html',params)

def worklimit_edit(request, wl_id):
	params={}
	worklimit=WorkLimit.objects.filter(id=int(wl_id))
	WorklimitFormset=modelformset_factory(WorkLimit,extra=0)
	if request.method=='POST':
		new_worklimit=WorklimitFormset(request.POST)
		cmd=request.POST['command'].lower()
		
		if cmd =='update' and new_worklimit.is_valid():
			new_worklimit.save()
		elif cmd =='delete':
			worklimit.delete()
		return redirect('resource_manage')
	else:
		params['worklimit_formset']=WorklimitFormset(queryset=worklimit)
	return render_to_response('tj3/worklimit_edit.html',params)
	
def journal_display(request, proj_id):
	params={}
	params['project']=proj = Project.objects.get(id=int(proj_id))
	
	# to get correct journals, we need to
	# project->task->booking->journal
	tasks=Task.tree.filter(project=proj)
	bookings=Booking.objects.filter(task__in=tasks).order_by('-when')
	
	# group by booking.when
	params['journals']=[]
	for key, group in groupby(bookings, lambda x: x.when):
		params['journals'].append((key,[Journal.objects.filter(booking=b) for b in group]))

	return render_to_response('tj3/journal_display.html',params)

def timeoff_create(request,resource_id):
	params={}
	params['resource']=resource=Resource.objects.get(id=int(resource_id))	
	TimeoffFormset=modelformset_factory(TimeOff,extra=1,exclude=('resource',))

	if request.method=='POST':
		new_timeoff=TimeoffFormset(request.POST)
		if new_timeoff.is_valid(): 
			t=new_timeoff.save(commit=False)[0]
			t.resource=resource
			t.save()
		return redirect('timeoff_create', resource_id=resource.id)
	else:
		params['timeoffs']=TimeOff.objects.filter(resource=resource).order_by('-end')
		params['timeoff_formset']=TimeoffFormset(queryset=TimeOff.objects.none())
		return render_to_response('tj3/timeoff_create.html',params)	

def timeoff_delete(request, timeoff_id):
	params={}
	params['resource']=timeoff=TimeOff.objects.get(id=int(timeoff_id))
	resource_id=timeoff.resource.id
	timeoff.delete()
	return redirect('timeoff_create', resource_id=resource_id)
	
def report_manage(request, proj_id):
	params={}
	params['project']=project=Project.objects.get(id=int(proj_id))
	params['taskreports']=TaskReport.objects.filter(project=project)
	params['resourcereports']=ResourceReport.objects.filter(project=project)
	
	return render_to_response('tj3/report_manage.html',params)

def taskreport_create(request, proj_id):
	params={}
	params['project']=project=Project.objects.get(id=int(proj_id))
	TaskReportFormset=modelformset_factory(TaskReport,exclude=('project',),extra=1)

	if request.method=='POST':
		new_taskreport=TaskReportFormset(request.POST)
		if new_taskreport.is_valid(): 
			t=new_taskreport.save(commit=False)[0]
			if not len(t.cols): t.cols=','.join(DEFAULT_TASK_COLUMNS)
			t.project=project
			t.save()
			
		return redirect('report_manage', proj_id=project.id)
	else:
		params['taskreport_formset']=TaskReportFormset(queryset=TaskReport.objects.none())
		return render_to_response('tj3/taskreport_create.html',params)	

def resourcereport_create(request, proj_id):
	params={}
	params['project']=project=Project.objects.get(id=int(proj_id))
	ResourceReportFormset=modelformset_factory(ResourceReport,exclude=('project',),extra=1)

	if request.method=='POST':		
		new_resourcereport=ResourceReportFormset(request.POST)
		if new_resourcereport.is_valid(): 
			t=new_resourcereport.save(commit=False)[0]
			if not len(t.cols): t.cols=','.join(DEFAULT_RESOURCE_COLUMNS)
			t.project=project
			t.save()
			
		return redirect('report_manage', proj_id=project.id)
	else:
		params['resourcereport_formset']=ResourceReportFormset(queryset=ResourceReport.objects.none())
		return render_to_response('tj3/resourcereport_create.html',params)	
	
def taskreport_edit (request, id):
	params={}
	tr=TaskReport.objects.filter(id=int(id))
	params['taskreport']=tr[0]
	params['project']=proj=tr[0].project
	TaskReportFormset=modelformset_factory(TaskReport,exclude=('project',),extra=0)	
	
	if request.method=='POST':
		new=TaskReportFormset(request.POST)
		if request.POST['command'].lower()=='update' and new.is_valid():
			new.save()
		return redirect('report_manage', proj_id=proj.id)
	else:
		params['report_formset']=TaskReportFormset(queryset=tr)
		
	return render_to_response('tj3/taskreport_edit.html',params)

def resourcereport_edit (request, id):
	params={}
	tr=ResourceReport.objects.filter(id=int(id))
	params['project']=proj=tr[0].project
	params['resourcereport']=tr=ResourceReport.objects.filter(id=int(id))
	ResourceReportFormset=modelformset_factory(ResourceReport,extra=0)
	
	if request.method=='POST':
		new=ResourceReportFormset(request.POST)
		if request.POST['command'].lower()=='update' and new.is_valid():
			new.save()
		return redirect('report_manage', proj_id=proj.id)
	else:
		params['report_formset']=ResourceReportFormset(queryset=tr)		
	return render_to_response('tj3/resourcereport_edit.html',params)

def resourcereport_delete(request, id):
	params={}
	params['resourcereport']=rr=ResourceReport.objects.get(id=int(id))
	params['project']=proj=rr.project
	rr.delete()
	return redirect('report_manage', proj_id=proj.id)

def resourcereport_copy(request,id):
	params={}
	params['resourcereport']=rr=ResourceReport.objects.get(id=int(id))
	params['project']=proj=rr.project
	rr.id=None
	rr.pk=None
	rr.save()
	return redirect('report_manage', proj_id=proj.id)


def taskreport_delete(request, id):
	params={}
	params['taskreport']=tr=TaskReport.objects.get(id=int(id))
	params['project']=proj=tr.project
	tr.delete()
	return redirect('report_manage', proj_id=proj.id)

def taskreport_copy(request,id):
	params={}
	params['taskreport']=tr=TaskReport.objects.get(id=int(id))
	params['project']=proj=tr.project
	tr.id=None
	tr.pk=None
	tr.name=tr.name + ' (copy)'
	tr.save()
	return redirect('report_manage', proj_id=proj.id)


def task_create(request):
	params={}
	if request.is_ajax():
		if request.method == 'POST':
			e = simplejson.loads(request.POST.get('data',False))
			params['project']=proj=Project.objects.get(id=int(e['proj_id']))
			
			task=Task(name=e['name'],description=e['description'])
			task.project=proj
			task.save()
			
			# create budgets
			for s in proj.scenario.get_descendants(include_self=true):
				b=TaskBudget(scenario=s,task=task)
				b.save()
		
	return HttpResponse('')

def project_create(request):
	params={}
	time_format = "%m/%d/%Y"
	
	if request.is_ajax():
		if request.method == 'POST':
			e = simplejson.loads(request.POST.get('data',False))
			start=datetime.fromtimestamp(time.mktime(time.strptime(e['start'], time_format)))
			end=datetime.fromtimestamp(time.mktime(time.strptime(e['end'], time_format)))
			proj=Project(name=e['name'],description=e['description'],start=start,end=end)
			proj.scenario=ProjectScenario.tree.root_nodes()[0]
			proj.save()
	return HttpResponse('here')
	
def project_email (request):
	params={}
	if request.is_ajax():
		if request.method == 'POST':
			e = simplejson.loads(request.POST.get('data',False))
			params['project']=proj=Project.objects.get(id=int(e['proj_id']))
			params['scenario']=scenario=ProjectScenario.objects.get(id=int(e['scenario_id']))
			
			# read resource load
			loads=ResourceLoad.objects.filter(project=proj, when__gte=date.today().isoformat(), scenario=scenario)
			#loads=ResourceLoad.objects.filter(project=proj, scenario=scenario)
			resources=list(set([l.resource for l in loads]))
			
			# Compose email msg
			emails_sent=[]
			for r in resources:
				# the actual email message
				msg = 'Hi %s,<p>A reminder that the following tasks have been scheduled for you:' % capwords(r.first_name)
				
				aa=[(l.when,l) for l in loads.filter(resource=r).order_by('when')]
				for key, group in groupby(aa, lambda x: x[0]):
					msg += '<h4>%s</h4>'% key
					msg += '<ul>'
					for a in group:
						# find task by searching task_name as full_path
						msg += '<li><font color=red>%s hours</font>, <font color=blue>%s</font>' % (a[1].load, a[1].task.name)
					msg += '</ul>'
					
				msg += '''<p>Best regards,<p>%s Project''' % proj.name
				subject = '%s Project Work Schedule' % proj.name
				from_email='fxia@crunchtime.com'
				
				# send email
				if e['to_resource']:
					#return HttpResponse('to_resource')
					if len(r.email):
						m = EmailMultiAlternatives(subject, '', from_email, [r.email])
						m.attach_alternative(msg, "text/html")
						m.send()
						emails_sent.append([r.first_name, r.last_name, r.email])
				for to in e['emails'].split(','):
					if to:
						m = EmailMultiAlternatives(subject, '', from_email, [to])
						m.attach_alternative(msg, "text/html")
						m.send()
						emails_sent.append([r.first_name, r.last_name,to])
			return HttpResponse(simplejson.dumps(emails_sent),mimetype='application/json')		


###################################################
# Need more cleanups
###################################################

def resource_allocate_create():
	params={}
	# New AllocateResource form
	AllocateResourceFormset=modelformset_factory(AllocateResource, exclude=('task',),extra=1)
	if request.method=='POST':
		cmd = request.POST['command'].lower()
		
		if cmd == 'create':
			# if we created a new allocate_resource, wow!
			new_alloc=AllocateResourceFormset(request.POST)
			if new_alloc.is_valid(): 
				alloc=new_alloc.save()[0]
				alloc.task.add(task)
				alloc.save()
	else:
		params['alloc_formset']=AllocateResourceFormset(queryset=AllocateResource.objects.none())
		return render_to_response('tj3/resource_allocate.html',params)

def resource_allocate_edit(request, alloc_id):
	params={}
	alloc=AllocateResource.objects.filter(id=int(alloc_id))
	AllocateFormset=modelformset_factory(AllocateResource,extra=0)
	if request.method=='POST':
		new_alloc=AllocateFormset(request.POST)
		cmd=request.POST['command'].lower()
		
		if cmd =='update' and new_alloc.is_valid():
			new_alloc.save()
		elif cmd =='delete':
			alloc[0].delete()
		return redirect('resource_manage')
	else:
		params['alloc_formset']=AllocateFormset(queryset=worklimit)
	return render_to_response('tj3/resource_allocate_edit.html',params)

def import_freemind (request):
	params = {}
	
	if request.method == 'POST':
		form = UploadFileForm(request.POST, request.FILES)
		if form.is_valid():
			ImportFreemind(request.FILES['file'])
						# do sth about the uploaded data
			return redirect('task_hierarchy')
		else:
			return redirect('home')
	else:
		params['form']=form = UploadFileForm()
	return render_to_response('tj3/import_freemind.html',params)

def export_freemind(request):
	params={}
	params['select_project']=ProjectSelectForm()
	if request.method=='POST':
		params['project']=proj = Project.objects.get(id=int(request.POST['project_list']))
		tasks = Task.tree.filter(project=proj).order_by('created')
		mm = TaskDumpToFreemind(proj, tasks)	
		return HttpResponse(mm,mimetype='application/octet-stream')
	else:
		return render_to_response('tj3/export_freemind.html',params,context_instance=RequestContext(request))


